import Taro from '@tarojs/taro'
// import md5 from 'md5'

import config from '../config'

import UserAuth from '../models/UserAuth'

import sign from './sign'

export interface BaseData<T> {
  code: number
  msg: string
  data: T
}
export type AnyData = BaseData<any>
export interface ListData<T> extends BaseData<T[]> {
  count: number
}
/**
 * 相应data
 */
export type Options = Omit<Param, 'url'>

/**
 * 请求参数
 */
export interface CustomParam {
  notLogin?: boolean
  catchErr?: boolean
  catchErrCodes?: number[]
}
export type Param = Taro.request.Param & CustomParam

export type Request<T> = (param: Param) => Promise<T>

/**
 * 当前登录会话
 */
let currentUserAuth: UserAuth | undefined
/**
 * 等待登录promise
 */
const waitLoginPromises: Array<{
  resolve: () => any
  reject: () => any
}> = []
/**
 * request
 */
export async function request<T extends BaseData<any>>(param: Param): Promise<T> {
  try {
    // 未登录, 等待登录
    if (!param.notLogin && !currentUserAuth) {
      await new Promise((resolve, reject) => {
        const promise = { resolve, reject }
        waitLoginPromises.push(promise)
      })
    }

    let data = param.data
    if (!param.notLogin) {
      if (!currentUserAuth) throw new Error('not user auth')
      data = sign({ userId: currentUserAuth.uid, token: currentUserAuth.credential }, data)
    }

    const resp = await wxRequest<T>({
      ...param,
      url: `${config.apiUrl}${param.url}`,
      header: {
        'content-type': 'application/x-www-form-urlencoded',
        ...param.header,
      },
      data,
    })

    if (isError(resp)) {
      throw resp
    }

    return resp
  } catch (error) {
    showError(error, param)
    if (error && error.errCode !== undefined) {
      throw error
    }
    if (process.env.NODE_ENV !== 'production') {
      console.error(error)
    }
    throw {
      errCode: 500,
      errMsg: '服务器繁忙',
    }
  }
}

function showError(error: any, param: Param) {
  if (config.online) return
  if (error && param.catchErr) {
    if (
      !param.catchErrCodes ||
      param.catchErrCodes.length === 0 ||
      param.catchErrCodes.some(errCode => error.errCode === errCode)
    ) {
      return
    }
  }
  let content
  try {
    content = JSON.stringify(error)
  } catch (e) {
    content = ''
  }
  Taro.hideLoading()
  Taro.showModal({ title: '接口异常', content, showCancel: false })
}

/**
 * 微信的请求，可配置重试
 */
async function wxRequest<T>(params: Taro.request.Param, retry: number = 5, interval: number = 2000): Promise<T> {
  try {
    const res = await Taro.request<T>(params)
    if (res.statusCode !== 200 && res.statusCode !== 204) {
      throw res.data
    }
    return res.data
  } catch (error) {
    console.error(error)

    if (retry > 0) {
      await new Promise(resolve => setTimeout(resolve, interval))
      return wxRequest<T>(params, retry - 1, interval)
    }
    throw error
  }
}

export function createRequest<A, T extends BaseData<any>>(argToParams: (param: A) => Param) {
  return (param: A, options?: Options) => {
    return request<T>({ ...argToParams(param), ...options })
  }
}
/**
 * 是否错误
 */
export function isError<T>(data: BaseData<T>): data is BaseData<never> {
  return data && data.code !== 0
}

/**
 * 是否没错
 */
export function isNotError<T>(data: BaseData<T>): data is BaseData<T> {
  return !isError(data)
}

/**
 * 登录成功
 */
export const loginSuccess = (userAuth: UserAuth) => {
  currentUserAuth = userAuth

  while (waitLoginPromises.length) {
    const promise = waitLoginPromises.shift()
    if (promise && promise.resolve) {
      try {
        promise.resolve()
      } catch (error) {
        console.error(error)
      }
    }
  }
}

export default request
